<html>
    <head></head>
    <body>
        <div id="app">
            <button @click="count++" @mouseenter="count++">+</button>
            <button @click="count--">-</button>

            <span v-text="(1 + count) * 2" v-show="count < 10"></span>
            <span v-text="count * 2"></span>
        </div>
    </body>

    <script>
        window.Vue = {
            el: "#app",

            data: {
                count: 0,
            },

            directives: {
                "v-text": (el, value) => {
                    el.innerText = value;
                },
                "v-show": (el, value) => {
                    el.style.display = value ? "block" : "none";
                },
            },

            init() {
                this.root = document.querySelector(this.el);

                this.proxyData = this.observe(this.data);

                this.refreshDom();
                this.registerListeners();
            },

            observe(data) {
                var self = this;
                return new Proxy(data, {
                    set(target, key, value) {
                        target[key] = value;

                        self.refreshDom();
                    },
                });
            },

            registerListeners() {
                this.walkDom(this.root, (el) => {
                    Array.from(el.attributes).forEach((attribute) => {
                        if (!attribute.name.startsWith("@")) return;

                        let event = attribute.name.replace("@", "");

                        let expression = attribute.value;

                        with (this.proxyData) {
                            el.addEventListener(event, () => {
                                eval(expression);
                            });
                        }
                    });
                });
            },

            refreshDom() {
                this.walkDom(this.root, (el) => {
                    Array.from(el.attributes).forEach((attribute) => {
                        if (!Object.keys(this.directives).includes(attribute.name))
                            return;

                        with (this.proxyData) {
                            this.directives[attribute.name](
                                el,
                                eval(attribute.value)
                            );
                        }
                    });
                });
            },

            walkDom(el, callback) {
                callback(el);

                el = el.firstElementChild;

                while (el) {
                    this.walkDom(el, callback);

                    el = el.nextElementSibling;
                }
            },
        };

        window.Vue.init();
    </script>
</html>
